package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/lib/pq"
)

const (
	host     = "localhost"
	port     = 5432
	user     = "admin"
	password = "admin"
	dbname   = "testdb"
)

type Teacher struct {
	id   int
	name string
	age  int
}

func main() {
	psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
		"password=%s dbname=%s sslmode=disable",
		host, port, user, password, dbname)
	db, err := sql.Open("postgres", psqlInfo)
	if err != nil {
		panic(err)
	}
	defer db.Close()

	err = db.Ping()
	if err != nil {
		panic(err)
	}

	fmt.Println("Successfully connected!")

	// 	sqlStatement := `
	// INSERT INTO t123 (name, age)
	// VALUES ($1, $2)
	// RETURNING id`
	// id := 0
	// err = db.QueryRow(sqlStatement, "小军", 18).Scan(&id)
	// if err != nil {
	// 	panic(err)
	// }
	/////////////////////////////////
	// stmt, err := db.Prepare(`insert into t123 (name,age) values (?, ?)`)
	// if err != nil {
	// 	log.Fatal(err)
	// }
	// rs, err := stmt.Exec("er", 19)
	// if err != nil {
	// 	log.Fatal(err)
	// }
	////////////////////////////////////
	// sqlstring := `INSERT INTO t123(name,age) VALUES('tom', 12)`
	// result, err := db.Exec(sqlstring)
	// if err != nil {
	// 	log.Fatal(err)
	// }
	//////////////////////////////////////
	st, err := db.Prepare(`INSERT INTO t123(name,age) VALUES($1, $2) RETURNING id`)
	if err != nil {
		log.Fatal(err)
	}
	defer st.Close()
	// rs, err := st.Exec("tom", 12)
	_, err = st.Exec("tom", 12)
	if err != nil {
		log.Fatal(err)
	}

	// if LastInsertId, err := rs.LastInsertId(); nil == err {
	// 	fmt.Println("LastInsertId:", LastInsertId)
	// }
	var lastid int
	err = db.QueryRow(`SELECT currval('t123_id_seq')`).Scan(&lastid)
	checkErr(err)
	fmt.Println("LastInsertId:", lastid)
	// log.Fatal(rs)
}

func checkErr(err error) {
	if err != nil {
		panic(err)
	}
}

//PostgreSQL使用$N作为占位符，N是一个从1开始递增的整数，代表参数的位置，方便参数的重复使用。MySQL使用?作为占位符，SQLite两种占位符都可以，而Oracle则使用:param1的形式。

// db.Exec(`CREATE TABLE test_users(id INTEGER PRIMARY KEY ,name TEXT);`)
// db.Exec(`TRUNCATE test_users;`)
// stmt, err := db.Prepare(`INSERT INTO test_users(id,name) VALUES ($1,$2) RETURNING id`)
// if err != nil {
//     fmt.Println(err.Error())
// }
// var returnID int
// err = stmt.QueryRow(4, "Alice").Scan(&returnID)
// if err != nil {
//     fmt.Println(err)
// } else {
//     fmt.Println(returnID)
// }
// 同样的语句使用Exec和Query执行有巨大的差别。如上文所述，Query会返回结果集Rows，而存在未读取数据的Rows其实会占用底层连接直到rows.Close()为止。因此，使用Query但不读取返回结果，会导致底层连接永远无法释放。database/sql期望用户能够用完就把连接还回来，所以这样的用法很快就会导致资源耗尽（连接过多）。所以，应该用Exec的语句绝不可用Query来执行。

// 如果id是serial类型的，则可以通过currval('table_name_id_seq')方法获得当前插入记录的id，这在postgresql的存储过程（function）中有用。

// nextval('table_name_id_seq')自然是获得下一个id（还没有发生）
